package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.domain.ProductSkuVo;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.mapper.ProductSkuMapper;
import com.spzx.product.query.SkuQuery;
import com.spzx.product.service.IProductSkuService;
import com.spzx.product.api.domain.SkuPriceVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 商品sku 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2024-12-20
 */
@Service
public class ProductSkuServiceImpl extends ServiceImpl<ProductSkuMapper, ProductSku> implements IProductSkuService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public List<ProductSkuVo> getTopSale() {
        return baseMapper.selectTopSale();
    }

    @Override
    public List<ProductSkuVo> selectProductSkuList(SkuQuery skuQuery) {
        return baseMapper.selectProductSkuList(skuQuery);
    }

    @Override
    public SkuPriceVo getSkuPrice(Long skuId) {
        ProductSku productSku = baseMapper.selectById(skuId);

        SkuPriceVo skuPriceVo = new SkuPriceVo();

        skuPriceVo.setMarketPrice(productSku.getMarketPrice());
        skuPriceVo.setSalePrice(productSku.getSalePrice());
        skuPriceVo.setSkuId(skuId);

        return skuPriceVo;
    }

    @Override
    public Map<String, Long> getSkuSpecValueMap(Long id) {
        LambdaQueryWrapper<ProductSku> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ProductSku::getProductId,id)
                .select(ProductSku::getSkuSpec,ProductSku::getId);
        List<ProductSku> productSkuList = baseMapper.selectList(queryWrapper);

        HashMap<String, Long> map = new HashMap();
        productSkuList.forEach(productSku -> {
            map.put(productSku.getSkuSpec(), productSku.getId());
        });
        return map;
    }

    /**
     * 获取商品sku详情信息
     * @param skuId
     * @return
     */
    @Override
    public ProductSku getProductSku(Long skuId) {


        try {

            String key = "product:sku:data";
            Boolean isExist = redisTemplate.opsForValue().getBit(key, skuId);
            if(!isExist){
                throw new ServiceException("商品不存在", 555);
            }

            //获取锁
            String lockKey = "product:sku:lock" + skuId;
            String lockValue = UUID.randomUUID().toString().replaceAll("-", "");
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
            if(flag){
                try {

                    //业务代码
                    //先从缓存中获取商品，如果存在则直接返回，然后将数据存入缓存
                    String dataKey = "product:sku:" + skuId;
                    ProductSku productSku = (ProductSku)redisTemplate.opsForValue().get(dataKey);
                    if(productSku != null){
                        log.warn("命中缓存");
                        return productSku;
                    }

                    //如果不存在，则查询数据库
                    log.warn("未命中缓存，查询数据库");
                    productSku = baseMapper.selectById(skuId);

                    //如果职位null，则存储一个new初始化对象
                    long ttl = productSku == null ? 60 : 10*60; //十分钟
                    if(productSku == null){
                        productSku = new ProductSku();
                    }

                    redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.SECONDS);

                    return productSku;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    //释放锁
                    String script = """
                        if redis.call('get', KEYS[1]) == ARGV[1] then
                            return redis.call('del', KEYS[1])
                        else
                            return 0
                        end
                        """;
                    Long lock = (Long)redisTemplate.execute(
                            new DefaultRedisScript<>(script , Long.class),
                            Arrays.asList(lockKey),
                            lockValue);
                    /*DefaultRedisScript<Boolean> defaultRedisScript = new DefaultRedisScript<>();
                    defaultRedisScript.setScriptText(script);
                    defaultRedisScript.setResultType(Boolean.class);
                    redisTemplate.execute(defaultRedisScript, Arrays.asList(lockKey), lockValue);*/
                    System.out.println("释放锁的结果为：" + lock);
                }
            }else{
                //获取锁失败，则自旋
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return this.getProductSku(skuId);
            }

        } catch (ServiceException e) {

            if(e.getCode().intValue() == 555){
                throw e;
            }else{
                log.error("获取商品详情异常{}", e);
                return baseMapper.selectById(skuId);
            }
        }catch (Exception e) {
            log.error("获取商品详情异常{}", e);
            return baseMapper.selectById(skuId);
        }
    }

    @Override
    public List<SkuPriceVo> getSkuPriceList(List<Long> skuIdList) {
        List<ProductSku> productSkuList = baseMapper.selectList(new LambdaQueryWrapper<ProductSku>().
                in(ProductSku::getId, skuIdList)
                .select(ProductSku::getId, ProductSku::getSalePrice));
        return productSkuList.stream().map(item -> {
            SkuPriceVo skuPrice = new SkuPriceVo();
            skuPrice.setSkuId(item.getId());
            skuPrice.setSalePrice(item.getSalePrice());
            return skuPrice;
        }).collect(Collectors.toList());
    }


}


